package cn.siat.gene.service;

import cn.hutool.core.util.ObjectUtil;
import cn.siat.common.core.constant.CacheConstants;
import cn.siat.common.redis.service.RedisService;
import cn.siat.gene.domain.SequenceOperate;
import cn.siat.gene.mapper.SequenceOperateMapper;
import com.alibaba.fastjson2.JSON;
import cn.siat.common.core.web.domain.AjaxResult;
import cn.siat.gene.constant.GenbankAnnotationKey;
import cn.siat.gene.constant.OperateEnum;
import cn.siat.gene.domain.Feature;
import cn.siat.gene.domain.Programme;
import cn.siat.gene.domain.Sequence;
import cn.siat.gene.mapper.FeatureMapper;
import cn.siat.gene.mapper.ProgrammeMapper;
import cn.siat.gene.mapper.SequenceMapper;
import cn.siat.gene.model.Location;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

@Service
@RequiredArgsConstructor
@Slf4j
public class FeatureService {
    private final FeatureMapper featureMapper;
    private final RedisService redisService;
    private final SequenceOperateMapper sequenceOperateMapper;
    @Transactional(rollbackFor = Exception.class)
    public void rollBack(Long sequenceId, Long operateId) {
        String operateIdStr = operateId.toString();
        List<Feature> list = featureMapper.listBySequenceId(sequenceId);
        List<Long> idList = new ArrayList<>(list.size());
        List<Feature> saveList = new ArrayList<>(list.size());
        for (Feature feature : list) {
            Map<String, String> history = feature.getHistory();
            if (history != null && history.containsKey(operateIdStr)) {
                idList.add(feature.getId());
                Feature featureHistory = JSON.parseObject(history.get(operateIdStr), Feature.class);
                saveList.add(featureHistory);
            }
            if(feature.getOperateId()!=null && operateId<=feature.getOperateId()){
                idList.add(feature.getId());
            }
        }
        this.removeFeatureList(sequenceId,idList);
        this.saveFeatureList(saveList);
    }

    public AjaxResult saveFeatureList(List<Feature> featureList) {
        if(featureList.isEmpty()){
            return AjaxResult.success(new ArrayList<>());
        }
        for (Feature feature : featureList) {
            dealFeature(feature);
        }
        SequenceOperate operate = sequenceOperateMapper.getLatest(new SequenceOperate().setSequenceId(featureList.get(0).getSequenceId()));
        List<Long> idList = new ArrayList<>(featureList.size());
        List<Feature> saveList = new ArrayList<>();
        Iterator<Feature> iterable = featureList.iterator();
        while (iterable.hasNext()){
            Feature saveFeature = iterable.next();
            if(operate!=null){
                saveFeature.setOperateId(operate.getId());
            }
            saveList.add(saveFeature);
            if(saveList.size()>2000||!iterable.hasNext()){
                featureMapper.saveList(saveList);
                for (Feature feature : saveList) {
                    idList.add(feature.getId());
                }
                saveList.clear();
            }
        }
        return AjaxResult.success(idList);
    }

    private void dealFeature(Feature feature) {
        if (feature.getStrand() == null) {
            feature.setStrand(1);
        }
        if (feature.getName() == null) {
            feature.setName(GenbankAnnotationKey.DEFAULT_FEATURE_NAME);
        }
        if (feature.getIsTemporary() == null) {
            feature.setIsTemporary(true);
        }
        if(feature.getSort()==null){
            if (Boolean.TRUE.equals(feature.getIsTemporary())) {
                feature.setSort(1);
            } else {
                feature.setSort(10);
            }
        }
        if (feature.getLocations() == null) {
            List<Location> list = new ArrayList<>();
            Location location = new Location(feature.getStart(), feature.getEnd());
            list.add(location);
            feature.setLocations(list);
        }
    }

    public AjaxResult updateFeature(Feature feature) {
        if (feature.getId() == null) {
            return AjaxResult.error("保存feature失败");
        }
        featureMapper.update(feature);
        return AjaxResult.success();
    }

    /**
     * 删除feature(真删除)
     *
     * @param sequenceId
     * @param idList
     * @return
     */
    public int removeFeatureList(Long sequenceId, List<Long> idList) {
        int count = 0;
        List<Long> removeList = new ArrayList<>(2000);
        // 2k一删除
        Iterator<Long> iterable = idList.iterator();
        while (iterable.hasNext()){
            removeList.add(iterable.next());
            if(removeList.size()>2000||!iterable.hasNext()){
                int c = featureMapper.removeByIdList(sequenceId, removeList);
                count+=c;
                removeList.clear();
            }
        }
        return count;
    }

    /**
     * 假删除
     * @param idList
     * @param isTemporary
     */
    public void removeBySequenceIdList(List<Long> idList, Boolean isTemporary) {
        // 2k一删除
        List<Long> list = new ArrayList<>(2000);
        Iterator<Long> iterable = idList.iterator();
        while (iterable.hasNext()){
            list.add(iterable.next());
            if(list.size()>2000||!iterable.hasNext()){
                featureMapper.removeBySequenceIdList(list, isTemporary);
                list.clear();
            }
        }
    }

    /**
     * @param param
     * @param operateEnum
     * @param operateId
     */
    public void changeFeatureIndex(Feature param, OperateEnum operateEnum, Long operateId,boolean isFinal) {
        if(param.getStart()>param.getEnd()){
            int paramStart = param.getStart(), paramEnd = param.getEnd();
            param.setStart(paramStart).setEnd(param.getOldSequenceLength());
            operateFeatureIndex(param, operateEnum, operateId,false);
            param.setStart(0).setEnd(paramEnd);
            operateFeatureIndex(param, operateEnum, operateId,operateEnum == OperateEnum.REPLACE?false:isFinal);
            if (operateEnum == OperateEnum.REPLACE) {
                operateEnum = OperateEnum.INSERT;
                param.setEnd(param.getSequenceLength());
                operateFeatureIndex(param, operateEnum, operateId,isFinal);
            }
        }else{
            if (operateEnum == OperateEnum.REPLACE) {
                operateFeatureIndex(param, OperateEnum.REPLACE, operateId,false);
                operateEnum = OperateEnum.INSERT;
                param.setEnd(param.getStart() + param.getSequenceLength());
            }
            operateFeatureIndex(param, operateEnum, operateId,isFinal);
        }

    }
    public List<Feature> listFeatureFromRedis(Long sequenceId){
        List<Feature> list = redisService.getCacheList(CacheConstants.PREFIX_FEATURE_LIST+sequenceId);
        for (Feature feature : list) {
            Map<String,List<String>> m = feature.getNotes();
            if(m!=null){
                m.remove("@type");
            }
        }
        return list;
    }
    public void cacheFeatureList(Long sequenceId,List<Feature> list){
        this.removeFeatureListFromCache(sequenceId);
        redisService.setCacheList(CacheConstants.PREFIX_FEATURE_LIST+sequenceId,list);
    }
    public void removeFeatureListFromCache(Long sequenceId){
        redisService.deleteObject(CacheConstants.PREFIX_FEATURE_LIST+sequenceId);
    }
    private void operateFeatureIndex(Feature param, OperateEnum operateEnum, Long operateId,boolean isFinal) {
        // 待删除
        List<Long> idList;
        List<Feature> list = listFeatureFromRedis(param.getSequenceId());
        if(list==null||list.isEmpty()){
            return;
        }
        List<Feature> result = new ArrayList<>();
        int length = param.getEnd() - param.getStart();
        for (Feature oldFeature : list) {
            Feature updateFeature = new Feature(oldFeature);
            updateFeature.setSequenceId(param.getSequenceId());
            Map<String, String> history = oldFeature.getHistory();
            if (history == null) {
                history = new HashMap<>();
            }
            updateFeature.setHistory(history);
            List<Location> oldLocations = oldFeature.getLocations();
            // 每次操作都要存
            // 覆盖式更新
            List<Location> updateLocations = new ArrayList<>();
            updateFeature.setLocations(updateLocations);
            int paramStart = param.getStart(), paramEnd = param.getEnd();
            boolean isCircle = false;
            if (oldFeature.getStart() > oldFeature.getEnd()) {
                isCircle = true;
            }
            //1 完全在feature后面不用处理(包含等于),环形都需要处理
            if (paramStart >= oldFeature.getEnd() && !isCircle) {
                // 保存历史记录就可以
                updateFeature.setLocations(oldLocations);
            } else if (operateEnum == OperateEnum.INSERT) {
                // insert只看起点
                // 2左,circle要区分上半截下半截
                if ((!isCircle && paramStart < oldFeature.getStart()) || (paramStart < oldFeature.getStart() && paramStart >= oldFeature.getEnd())) {
                    updateFeature.setStart(oldFeature.getStart() + length);
                    if (isCircle) {
                        updateFeature.setEnd(oldFeature.getEnd());
                    } else {
                        updateFeature.setEnd(oldFeature.getEnd() + length);
                    }
                    for (Location oldLocation : oldLocations) {
                        int lStart = oldLocation.getStart() + length, lEnd = oldLocation.getEnd() + length;
                        if (isCircle) {
                            if (oldLocation.getStart() < oldFeature.getStart()) {
                                lStart = oldLocation.getStart();
                            }
                            if (oldLocation.getEnd() < oldFeature.getStart()) {
                                lEnd = oldLocation.getEnd();
                            }
                        }
                        updateLocations.add(new Location(lStart, lEnd));
                    }
                } else {
                    updateFeature.setName(oldFeature.getName() + "_" + operateEnum.getValue());
                    //3中
                    updateFeature.setStart(oldFeature.getStart());
                    updateFeature.setEnd(oldFeature.getEnd() + length);
                    if (isCircle) {
                        // 上半截
                        if (paramStart >= oldFeature.getStart()) {
                            updateFeature.setEnd(oldFeature.getEnd());
                        } else {
                            // 下半截
                            updateFeature.setStart(oldFeature.getStart() + length);
                        }
                    }
                    for (int i = 0; i < oldLocations.size(); i++) {
                        Location oldLocation = oldLocations.get(i);
                        Location updateLocation = new Location(oldLocation.getStart(), oldLocation.getEnd());
                        updateLocations.add(updateLocation);
                        if (isCircle && paramStart >= oldFeature.getStart()) {
                            // 上半截插入,下半截数据不用处理,start之前的不用处理
                            if (oldLocation.getStart() >= oldFeature.getStart() && oldLocation.getStart() >= paramStart) {
                                updateLocation.setStart(oldLocation.getStart() + length);
                            }
                            if (oldLocation.getEnd() >= oldFeature.getStart() && oldLocation.getEnd() >= paramStart) {
                                updateLocation.setEnd(oldLocation.getEnd() + length);
                            }
                        } else {
                            // 下半截插入,start之后全处理,同非circle
                            if (oldLocation.getStart() >= paramStart) {
                                updateLocation.setStart(oldLocation.getStart() + length);
                            }
                            if (oldLocation.getEnd() >= paramStart) {
                                updateLocation.setEnd(oldLocation.getEnd() + length);
                            }
                        }
                    }
                }
            } else {
                //2 完全在左边
                if ((!isCircle && paramEnd < oldFeature.getStart()) ||
                        (isCircle && paramStart < oldFeature.getStart() && paramStart >= oldFeature.getEnd() && paramEnd > oldFeature.getEnd() && paramEnd < oldFeature.getStart())) {
                    updateFeature.setStart(oldFeature.getStart() - length);
                    if (isCircle) {
                        updateFeature.setEnd(oldFeature.getEnd());
                    } else {
                        updateFeature.setEnd(oldFeature.getEnd() - length);
                    }
                    for (Location oldLocation : oldLocations) {
                        int lStart = oldLocation.getStart() - length, lEnd = oldLocation.getEnd() - length;
                        if (isCircle) {
                            if (oldLocation.getStart() < oldFeature.getStart()) {
                                lStart = oldLocation.getStart();
                            }
                            if (oldLocation.getEnd() < oldFeature.getStart()) {
                                lEnd = oldLocation.getEnd();
                            }
                        }
                        updateLocations.add(new Location(lStart, lEnd));
                    }
                } else if (paramStart <= oldFeature.getStart() && paramEnd >= oldFeature.getEnd() && !isCircle) {
                    //3覆盖
                    updateFeature.setStart(-1).setEnd(-1);
                } else if ((paramStart >= oldFeature.getStart() && paramEnd <= oldFeature.getEnd()) ||
                        (isCircle && (paramStart >= oldFeature.getStart() || paramEnd <= oldFeature.getEnd()))) {
                    updateFeature.setName(oldFeature.getName() + "_" + operateEnum.getValue());
                    //4完全在中间,(暂不考虑上下一起cut)
                    updateFeature.setStart(oldFeature.getStart());
                    updateFeature.setEnd(oldFeature.getEnd() - length);
                    if (isCircle) {
                        // 上半截
                        if (paramStart >= oldFeature.getStart()) {
                            updateFeature.setEnd(oldFeature.getEnd());
                        } else {
                            // 下半截
                            updateFeature.setStart(oldFeature.getStart() - length);
                        }
                    }
                    if (oldLocations.size() == 1) {
                        updateLocations.add(new Location(updateFeature.getStart(), updateFeature.getEnd()));
                    } else {
                        for (int i = 0; i < oldLocations.size(); i++) {
                            Location oldLocation = new Location(oldFeature.getLocations().get(i).getStart(), oldFeature.getLocations().get(i).getEnd());
                            if (paramStart <= oldLocation.getStart() && paramEnd >= oldLocation.getEnd() &&
                                    oldLocation.getEnd() >= oldLocation.getStart()) {
                                continue;
                            }
                            Location updateLocation = new Location(oldLocation.getStart(), oldLocation.getEnd());
                            if (isCircle && paramStart >= oldFeature.getStart()) {
                                // 上半截cut,下半截数据不用处理,start之前的不用处理
                                if (oldLocation.getStart() >= oldFeature.getStart() && oldLocation.getStart() >= paramStart) {
                                    int lStart = oldLocation.getStart() - length;
                                    if (lStart < paramStart) {
                                        lStart = paramStart;
                                    }
                                    updateLocation.setStart(lStart);
                                }
                                if (oldLocation.getEnd() >= oldFeature.getStart() && oldLocation.getEnd() >= paramStart) {
                                    int lEnd = oldLocation.getEnd() - length;
                                    if (lEnd <= paramStart) {
                                        lEnd = paramStart - 1;
                                    }
                                    updateLocation.setEnd(lEnd);
                                }
                            } else {
                                // 下半截cut,start之后全处理,同非circle
                                if (oldLocation.getStart() >= paramStart) {
                                    int lStart = oldLocation.getStart() - length;
                                    if (lStart < paramStart) {
                                        lStart = paramStart;
                                    }
                                    updateLocation.setStart(lStart);
                                }
                                if (oldLocation.getEnd() >= paramStart) {
                                    int lEnd = oldLocation.getEnd() - length;
                                    if (lEnd <= paramStart) {
                                        lEnd = paramStart - 1;
                                    }
                                    updateLocation.setEnd(lEnd);
                                }
                            }
                            updateLocations.add(updateLocation);
                        }
                    }
                } else if (paramStart >= oldFeature.getStart() ||
                        (isCircle && paramStart < oldFeature.getEnd() && paramEnd < oldFeature.getStart())) {
                    updateFeature.setName(oldFeature.getName() + "_" + operateEnum.getValue());
                    //5右边部分在外
                    updateFeature.setStart(oldFeature.getStart());
                    updateFeature.setEnd(paramStart - 1);
                    if (isCircle) {
                        updateFeature.setStart(oldFeature.getStart() - length);
                    }
                    if (oldLocations.size() <= 1) {
                        updateLocations.add(new Location(updateFeature.getStart(), updateFeature.getEnd()));
                    } else {
                        for (int i = 0; i < oldLocations.size(); i++) {
                            Location oldLocation = new Location(oldFeature.getLocations().get(i).getStart(), oldFeature.getLocations().get(i).getEnd());
                            if (paramStart <= oldLocation.getStart() && paramEnd >= oldLocation.getEnd() &&
                                    oldLocation.getEnd() >= oldLocation.getStart()) {
                                continue;
                            }
                            Location updateLocation = new Location(oldLocation.getStart(), oldLocation.getEnd());
                            // 下半截cut,start之后全处理,同非circle
                            if (oldLocation.getEnd() >= paramStart && oldLocation.getStart() <= paramStart) {
                                updateLocation.setEnd(paramStart - 1);
                            }
                            if (oldLocation.getStart() > paramEnd) {
                                updateLocation.setStart(updateLocation.getStart() - length);
                            }
                            if (oldLocation.getEnd() > paramEnd) {
                                updateLocation.setEnd(updateLocation.getEnd() - length);
                            }
                            updateLocations.add(updateLocation);
                        }
                    }
                    updateFeature.setEnd(updateLocations.get(updateLocations.size() - 1).getEnd());
                } else if ((!isCircle && paramStart < oldFeature.getStart()) || (isCircle && paramStart < oldFeature.getStart() && paramStart > oldFeature.getEnd())) {
                    updateFeature.setName(oldFeature.getName() + "_" + operateEnum.getValue());
                    //6左边部分在外
                    updateFeature.setStart(paramStart);
                    if (isCircle) {
                        updateFeature.setEnd(oldFeature.getEnd());
                    } else {
                        updateFeature.setEnd(oldFeature.getEnd() - length);
                    }
                    if (oldLocations.size() <= 1) {
                        updateLocations.add(new Location(updateFeature.getStart(), updateFeature.getEnd()));
                    } else {
                        for (int i = 0; i < oldLocations.size(); i++) {
                            Location oldLocation = new Location(oldFeature.getLocations().get(i).getStart(), oldFeature.getLocations().get(i).getEnd());
                            if (paramStart <= oldLocation.getStart() && paramEnd >= oldLocation.getEnd() &&
                                    oldLocation.getEnd() >= oldLocation.getStart()) {
                                continue;
                            }
                            Location updateLocation = new Location(oldLocation.getStart(), oldLocation.getEnd());
                            // 上半截cut,start之后全处理,
                            if (oldLocation.getStart() > paramEnd) {
                                int lStart = oldLocation.getStart() - length;
                                if (lStart < paramStart) {
                                    lStart = paramStart;
                                }
                                updateLocation.setStart(lStart);
                            }
                            if (oldLocation.getEnd() > paramEnd) {
                                updateLocation.setEnd(updateLocation.getEnd() - length);
                            }
                            updateLocations.add(updateLocation);
                        }
                    }
                    updateFeature.setStart(updateLocations.get(0).getStart());
                } else {
                    updateFeature.setName(oldFeature.getName() + "_" + operateEnum.getValue());
                    //7环形头尾删除, 先这样
                    updateFeature.setStart(paramStart);
                    updateFeature.setEnd(paramStart - 1);
                    for (int i = 0; i < oldLocations.size(); i++) {
                        Location oldLocation = new Location(oldFeature.getLocations().get(i).getStart(), oldFeature.getLocations().get(i).getEnd());
                        if (paramStart <= oldLocation.getStart() && paramEnd >= oldLocation.getEnd() &&
                                oldLocation.getEnd() >= oldLocation.getStart()) {
                            continue;
                        }
                        Location updateLocation = new Location(oldLocation.getStart(), oldLocation.getEnd());
                        if (oldLocation.getStart() >= paramStart) {
                            int lStart = oldLocation.getStart() - length;
                            if (lStart < paramStart) {
                                lStart = paramStart;
                            }
                            updateLocation.setStart(lStart);
                        }
                        if (oldLocation.getEnd() > paramStart) {
                            int lEnd = oldLocation.getEnd() - length;
                            if (lEnd < paramStart) {
                                lEnd = paramStart;
                            }
                            updateLocation.setEnd(lEnd);
                        }
                        updateLocations.add(updateLocation);
                    }
                }
            }
            if (updateLocations.size() > 0) {
                updateLocations.get(0).setStart(updateFeature.getStart());
                updateLocations.get(updateLocations.size() - 1).setEnd(updateFeature.getEnd());
                for (int i = 0; i < updateLocations.size(); i++) {
                    if (i != updateLocations.size() - 1) {
                        Location nextLocation = updateLocations.get(i + 1);
                        if (updateLocations.get(i).getEnd().equals(nextLocation.getStart())) {
                            updateLocations.get(i).setEnd(updateLocations.get(i).getEnd() - 1);
                        }
                    }
                }
            }
            // replace两次操作,更新一次历史就行
            if (!history.containsKey(operateId.toString())) {
                oldFeature.setHistory(null);
                history.put(operateId.toString(), JSON.toJSONString(oldFeature));
            } else {
                // 第二次操作不更新name
                updateFeature.setName(oldFeature.getName());
            }
            result.add(updateFeature);
        }
        idList = new ArrayList<>(result.size());
        for (Feature feature : result) {
            idList.add(feature.getId());
        }
        if(isFinal){
            this.removeFeatureList(param.getSequenceId(),idList);
            this.saveFeatureList(result);
        }else{
            this.cacheFeatureList(param.getSequenceId(),result);
        }
    }
}
